--[[

    Filename:	auxenv.lua

    Programmer:	aesiot

    Remark:

--]]

auxlib = require( "auxlib" )
auxmsg = require( "auxmsg" )
auxstr = require( "auxstr" )
auxthr = require( "auxthr" )
auxtmr = require( "auxtmr" )

local auxenv = { _VERSION = "1.0" }

--------------------------------------------------------------------------------

local function searchpath( name, path, ext )

	return package.searchpath( name, string.format( "%s/?.%s", ( path or auxenv.pathname ), ( ext or "lua" ) ) )
end

--------------------------------------------------------------------------------

function auxenv.require( modname )

	if package.loaded[ modname ] then

		return package.loaded[ modname ]
	end

	local path = searchpath( modname )

	local result = ( path and dofile( path ) )

	package.loaded[ modname ] = result

	return result or require( modname )
end

--------------------------------------------------------------------------------

function auxenv.add_library_path( pathname )

	if not auxlib.is_directory( pathname ) then

		return false
	end

	pathname = string.gsub( pathname, "([\\/]+)", '/' )

	package.path = package.path .. ';' .. pathname .. "/?.lua"

	return true
end

--------------------------------------------------------------------------------

function auxenv.do_file( name, path, ext )

	if not auxlib.is_regular_file( name ) then

		name = searchpath( name, path, ext )
	end

	return name and dofile( name )
end

--------------------------------------------------------------------------------

function auxenv.end_task()

	local name = vm.get_task_name()

	timer_remove_all()

	vm.task_delete()

	if "main" == name then

		os.raise( os.SIGTERM )
	end
end

--------------------------------------------------------------------------------

function auxenv.load_file( name, path, ext, mode, env )

	if not auxlib.is_regular_file( name ) then

		name = searchpath( name, path, ext )
	end

	return name and loadfile( name, mode, env )
end

--------------------------------------------------------------------------------

function auxenv.load_library( name, path )

	if not auxlib.is_regular_file( path ) then

		local cpath = string.format( "%s/?.so", ( path or auxenv.pathname ) )

		cpath = cpath .. ';' .. string.format( "%s/?.dll", ( path or auxenv.pathname ) )
		cpath = cpath .. ';' .. string.format( "%s/?.lib", ( path or auxenv.pathname ) )

		path = package.searchpath( name, cpath )
	end

	if not auxlib.is_regular_file( path ) then

		path = package.searchpath( name, package.cpath )
	end

	if not auxlib.is_regular_file( path ) then

		return nil
	end

	local fn = package.loadlib( path, string.format( "luaopen_%s", name ) ) or package.loadlib( path, name )

	if not auxlib.is_function( fn ) then

		return nil
	end

	return fn()
end

--------------------------------------------------------------------------------

function auxenv.path_split( path )

	local pathname = nil
	local filename = nil
	local extename = nil

	local is_separator = function( c )

		return 47 == c or 92 == c
	end

	if auxlib.is_string( path ) then

		local items = {}

		for item in string.gmatch( path, "[^/\\]+" ) do

			table.insert( items, item )
		end

		local ln = #items

		local lc = string.byte( path )

		local rc = string.byte( path, #path )

		if ln > 1 then

			if is_separator( lc ) and is_separator( rc ) then

				pathname = '/' .. table.concat( items, '/' )

			elseif is_separator( lc ) then

				pathname = '/' .. table.concat( items, '/', 1, ln - 1 )

				filename = items[ ln ]

			elseif is_separator( rc ) then

				pathname = table.concat( items, '/' )
			else

				pathname = table.concat( items, '/', 1, ln - 1 )

				filename = items[ ln ]
			end
		else

			if 0 == ln then

				pathname = auxlib.if_else( is_separator( lc ), '/', '' )

			elseif is_separator( lc ) and is_separator( rc ) then

				pathname = '/' .. items[ 1 ]

			elseif is_separator( lc ) then

				pathname = '/'

				filename = items[ 1 ]

			elseif is_separator( rc ) then

				pathname = items[ 1 ]
			else

				pathname = '.'

				filename = items[ 1 ]
			end
		end
	end

	if auxlib.is_string( filename ) then

		local items = {}

		for item in string.gmatch( filename, "[^.]+" ) do

			table.insert( items, item )
		end

		if #items > 1 then

			filename = table.concat( items, '.', 1, #items - 1 )

			extename = items[ #items ]
		end
	end

	return pathname, filename, extename
end

--------------------------------------------------------------------------------

function auxenv.print( level, ... )

	logger.print( auxlib.if_binary( level, logger.LEVEL_TRACE, auxlib.is_integer ), ... )
end

function auxenv.printf( level, format, ... )

	logger.print( auxlib.if_binary( level, logger.LEVEL_TRACE, auxlib.is_integer ), string.format( format, ... ) )
end

function auxenv.print_runtime_error( message )

	logger.print_multi(

		logger.LEVEL_WARNING, "[ WARN ] LUA RUNTIME ERROR",

		logger.LEVEL_DEBUG, string.format( "  # %s", message )
	)
end

--------------------------------------------------------------------------------

function auxenv.shell_read_all( cmdtxt, filter )

	auxlib.argcheck( auxlib.is_function( filter ), 2, "(function expected)" )

	return auxlib.pcall_else( function( file )

		local r = file:read( 'a' )

		file:close()

		return filter( r )

	end, function() return nil end, io.popen, cmdtxt )
end

--------------------------------------------------------------------------------

function auxenv.shell_read_line( cmdtxt, filter )

	auxlib.argcheck( auxlib.is_function( filter ), 2, "(function expected)" )

	return auxlib.pcall_else( function( file )

		for line in file:lines() do

			local r = filter( line )

			if nil ~= r then return r end
		end

		return nil

	end, function() return nil end, io.popen, cmdtxt )
end

--------------------------------------------------------------------------------

function auxenv.task_create( filename, taskname, ... )

	local rmmark = false

	repeat

		if auxlib.is_function( filename ) then

			local code = string.dump( filename )

			filename = os.mkstemp( nil, "lua" )

			if not filename then

				break
			end

			rmmark = true

			local file = io.open( filename, "wb" )

			if not file then

				break
			end

			file:write( code )

			file:flush()

			file:close()
		end

		if auxlib.is_regular_file( filename ) then

			if vm.task_create( taskname or filename, filename, ... ) then

				return true
			end
		else

			local pathname = searchpath( filename )

			if pathname and vm.task_create( taskname or filename, pathname, ... ) then

				return true
			end
		end

	until true

	if rmmark then

		os.remove( filename )
	end

	return false
end

--------------------------------------------------------------------------------

auxenv.pathname = auxenv.path_split( arg[ 1 ] )

print  = logger.trace
printf = auxenv.printf
printv = auxenv.print

do
	local result = {}

	setmetatable( result, {

		__index = auxenv,

		__metatable = "cannot get a protected metatable",

		__newindex = function()

			error( "cannot set a protected members", 2 )
		end
	} )

	return result
end
